use crate::contract::coolink_sdk::HttpdImpP2PCallBack;
use crate::contract::coolink_sdk::HttpdImpP2PContract;
use crate::contract::coolink_sdk::SDKApiResult;
use crate::imp::tonicrpc::tonicserverimp::reset_download_path_map;
use crate::model::sdk_api_result::SDKResultError;

use futures_core::Stream;
use tokio::{runtime::Builder, sync::mpsc::UnboundedSender, time::Instant};

use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::prelude::*;
use std::net::IpAddr;

use tonic::Streaming;
use tonic_inf::{
    api::{
        download_response::DownloadResult, tunnel_service_client::TunnelServiceClient,
        DownloadResponse, RangeDownloadRequest, ResponseBuffer, ResponseFail, ResponseStart,
    },
    Channel,
};

use crate::imp::tonicrpc::tonicserver::{
    _get_tonic_client, get_tunnel_creator, FlowControlInterceptor,
};
use crate::utils::ids;
use anyhow::anyhow;
use anyhow::Result;

// use tracing::Instrument;
use headers::{AcceptRanges, ContentLength, ContentRange, ContentType, HeaderMapExt, LastModified};
use warp::{
    filters::tonic::{bytes_range, bytes_range_pub, Cond, Conditionals},
    path::Tail,
    reject,
    reply::Response,
    Filter,
};

use tonic::codegen::InterceptedService;

use crate::imp::tonicrpc::pairinlanmap;
use crate::utils::symbol_link_util;
use http::StatusCode;
use hyper::Body;
use serde::{Deserialize, Serialize};
use state::Storage;
use std::time::{Duration, UNIX_EPOCH};

use url::Url;

// use crate::imp::rtm_sdk_imp::{call_attach_fn, call_detach_fn};
use crate::imp::tonicrpc::kcptunnel::tunnel::TUNNEL_ID_HTTP_END_FIX;

pub const SYMBOL_PREFIX: &str = "_sym_";

lazy_static! {
    static ref HTTP_STOP_SENDER: std::sync::RwLock<Option<(UnboundedSender<HttpCommand>, String)>> =
        std::sync::RwLock::new(None);

    // in a time only one http client can be supported(others will destroyed)!
    static ref TARGET_HTTP_MAP: tokio::sync::RwLock<HashMap<String, HttpTunnelItem>> = tokio::sync::RwLock::new(HashMap::new());

    static ref HTTP_LOCAL_ROOT_DIR: Storage<String> = Storage::new();
}

pub fn get_http_srv_root_dir() -> Option<String> {
    let result = if let Some(dir) = HTTP_LOCAL_ROOT_DIR.try_get() {
        let dir_path = (*dir).clone();
        Some(dir_path.to_owned())
    } else {
        None
    };
    result
}
// http command
enum HttpCommand {
    StopService,
    // ReturnClient(HttpTunnel),
}

#[derive(Clone)]
struct HttpTunnelItem {
    client: TunnelServiceClient<InterceptedService<Channel, FlowControlInterceptor>>,
    tunnel_id: String,
    identifier: String,
    created_time: Instant,
}

// get command sender from HTTP_STOP_SENDER rwlock
fn get_command_sender() -> Result<(UnboundedSender<HttpCommand>, String)> {
    let sender = HTTP_STOP_SENDER
        .try_read()
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;
    match sender.as_ref() {
        Some(sender) => Ok(sender.clone()),
        None => Err(anyhow::anyhow!(SDKResultError::ErrorNotHttpSrvStarted)),
    }
}

// take command sender from HTTP_STOP_SENDER rwlock
fn take_command_sender() -> Result<(UnboundedSender<HttpCommand>, String)> {
    let mut sender = HTTP_STOP_SENDER
        .try_write()
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;

    if let Err(err) = reset_download_path_map() {
        log::error!("reset_download_path_map err :{:?}", err);
    };

    match sender.take() {
        Some(sender) => Ok(sender),
        None => Err(anyhow::anyhow!(SDKResultError::ErrorNotHttpSrvStarted)),
    }
}

// get http host
pub fn get_http_host_inner() -> Result<String> {
    let sender = HTTP_STOP_SENDER
        .try_read()
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;

    match sender.as_ref() {
        Some((_sender, host)) => Ok(host.to_owned()),
        None => {
            anyhow::bail!(SDKResultError::ErrorNotHttpSrvStarted)
        }
    }
}

// check commnad receiver
fn check_command_receiver() -> Result<()> {
    let sender = HTTP_STOP_SENDER
        .try_read()
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;
    anyhow::ensure!(sender.is_none(), SDKResultError::ErrorHttpSrvAlreadyStarted);

    return Ok(());
}

// set command receiver
fn set_command_receiver(http_tx: UnboundedSender<HttpCommand>, host: String) -> Result<()> {
    let mut sender = HTTP_STOP_SENDER
        .try_write()
        .map_err(|e| anyhow::anyhow!(e.to_string()))?;
    anyhow::ensure!(sender.is_none(), SDKResultError::ErrorHttpSrvAlreadyStarted);
    let _ = sender.insert((http_tx, host));
    Ok(())
}

async fn destory_http_item(identify: String) {
    let http_item_opt = TARGET_HTTP_MAP.write().await.remove(&identify);
    if let Some(http_item) = http_item_opt {
        if let Some(mut creator) = get_tunnel_creator() {
            log::warn!("_remote_server destory_http_item identify {:?}", identify);
            if let Err(e) = creator.disconnect(&http_item.tunnel_id) {
                log::warn!(
                    "_remote_server destory_http_item creator.disconnect err: {:?}",
                    e
                );
            }
        }
    }
}

async fn clean_http_items() {
    for (identify, http_item) in TARGET_HTTP_MAP.write().await.drain() {
        if let Some(mut creator) = get_tunnel_creator() {
            log::warn!("_remote_server clean_http_items identify {:?}", identify);
            if let Err(e) = creator.disconnect(&http_item.tunnel_id) {
                log::warn!(
                    "_remote_server clean_http_items creator.disconnect err: {:?}",
                    e
                );
            }
        }
    }
}

async fn generate_tonic_client(target_id: &str, identify: String) -> Result<HttpTunnelItem> {
    let client_identify = format!("{}:{}", target_id.clone(), identify);

    let need_drop_values: Vec<_> = TARGET_HTTP_MAP
        .write()
        .await
        .iter()
        .filter(|&(k, _)| k != &client_identify)
        .map(|(k, _)| k.clone())
        .collect();

    for drop_value in need_drop_values {
        destory_http_item(drop_value.to_owned()).await;
    }

    if let Some(http_cli) = TARGET_HTTP_MAP
        .write()
        .await
        .get_mut(&client_identify.clone())
    {
        return Ok(http_cli.to_owned());
    };

    let tunnel_id = format!("{}{}", ids::id_generate_xbit(2), TUNNEL_ID_HTTP_END_FIX);

    let client = _get_tonic_client(&target_id, &tunnel_id).await?;

    let http_item = HttpTunnelItem {
        client,
        tunnel_id,
        identifier: client_identify.clone(),
        created_time: Instant::now(),
    };

    TARGET_HTTP_MAP
        .write()
        .await
        .insert(client_identify, http_item.clone());

    Ok(http_item)
}

fn tonic_stream(
    mut size: u64,
    mut resp: Streaming<DownloadResponse>,
    tunnel: HttpTunnelItem,
) -> impl Stream<Item = Result<Vec<u8>>> + Send {
    // tracing::info!(%tunnel.target_id, %tunnel.tunnel_id, %size, "tonic_stream start",);
    log::debug!(
        "tonic_stream start,identify = {},tunnel_id={},size={}",
        tunnel.identifier,
        tunnel.tunnel_id,
        size
    );

    async_stream::try_stream! {
        let mut has_finish = false;
        let mut err = None;
        let mut new_buffer = None;
        loop {
            match resp.message().await {
                Ok(result) => match result {
                    Some(DownloadResponse { download_result }) => match download_result {
                        Some(result) => match result {
                            DownloadResult::Start(info) => {
                                // tracing::info!(
                                //     "tonic_stream DownloadResult::Start info:{:?}", info
                                // );
                                log::debug!("tonic_stream DownloadResult::Start info:{:?}", info);
                            },
                            DownloadResult::Buffer(ResponseBuffer {buffer, is_finish}) => {
                                let len = buffer.len() as u64;
                                // log::trace!(
                                //     "tonic_stream buffer size: {}", len
                                // );
                                log::debug!("tonic_stream buffer size: {}",len);
                                if len >= size {
                                    // tracing::info!(%is_finish,
                                    //     "tonic_stream size == 0",
                                    // );
                                    log::debug!("tonic_stream size == 0 is_finish");

                                } else {
                                    size = size - len;
                                }
                                if buffer.len() > 0 {
                                    let _ = new_buffer.insert(buffer);
                                }
                                has_finish = is_finish;
                            },
                            DownloadResult::Fail(ResponseFail { code, msg }) => {
                                // tracing::info!("tonic_stream DownloadResult::Fail code: {} msg: {}", code, msg);
                                log::debug!("tonic_stream DownloadResult::Fail code: {} msg: {}", code, msg);
                                let e = Err(anyhow::anyhow!("tonic_stream DownloadResult::Fail code: {} msg: {}", code, msg));
                                let _ = err.insert(e);
                            }
                        }
                        None => {
                            log::error!("match download_result return None");
                        }
                    }
                    None => {
                        has_finish = true;
                    }
                }
                Err(e) => {
                   let _ =  err.insert(Err(anyhow::anyhow!(e)));
                }
            };
            if err.is_some() {
                has_finish = true;
            }
            if has_finish {
                // tracing::info!("tonic_stream has finish:");
                log::info!("tonic_stream has finish!");
                if err.is_some() {
                    // tracing::info!(%tunnel.target_id, %tunnel.tunnel_id, "has_finish err occur discconecting");
                    log::info!("{} {} has_finish err occur discconecting",tunnel.identifier,tunnel.tunnel_id);
                } else {
                    // tracing::info!(%tunnel.target_id, %tunnel.tunnel_id, "has_finish return client");
                    log::info!("{} {} has_finish return client",tunnel.identifier,tunnel.tunnel_id);
                    // return_tonic_client(tunnel).await;
                }
                if let Some(buffer) = new_buffer.take() {
                    yield buffer;
                }
                if let Some(e) = err.take() {
                    // tracing::error!(?e, "tonic_stream err:");
                    log::error!("tonic_stream err: tack e,e = {:?}",e);

                    e?;
                }
                break;
            } else {
                if let Some(buffer) = new_buffer.take() {
                    yield buffer;
                }
            }
        }

    }
}

// client request to remote ,and return stream for http calling.
async fn _remote_server(
    cond: Conditionals,
    target_id: String,
    relative_path: Option<String>,
    file_id: Option<String>,
) -> std::result::Result<impl warp::Reply, anyhow::Error> {
    // tracing::info!(?cond, %target_id, ?relative_path, "_remote_server start");

    let mut url_identify = "".to_owned();
    if relative_path.is_some() {
        url_identify = relative_path.clone().unwrap();
    } else if file_id.is_some() {
        url_identify = file_id.clone().unwrap();
    }

    let mut tunnel = generate_tonic_client(&target_id, url_identify.clone()).await?;

    let Conditionals { range, .. } = &cond;
    let (start, end) = bytes_range_pub(range.clone());
    let reqest = RangeDownloadRequest {
        need_sha256: false,
        relative_file_path: relative_path,
        file_id,
        start,
        end,
    };
    // tracing::info!("_remote_server reqest: {:?}", reqest);

    log::debug!("_remote_server reqest: {:?}", reqest);
    //  client create a request to remote server
    let mut stream = if let Ok(download_res) = tunnel.client.range_download(reqest.clone()).await {
        download_res.into_inner()
    } else {
        let mut tunnel = generate_tonic_client(&target_id, url_identify.clone()).await?;
        tunnel.client.range_download(reqest).await?.into_inner()
    };
    //  tunnel.client.range_download(reqest).await?.into_inner();

    // response
    let DownloadResponse { download_result } = stream
        .message()
        .await?
        .ok_or(anyhow::anyhow!("respone empty"))?;
    let download_result = download_result.ok_or(anyhow::anyhow!("download_result empty"))?;
    let start = match download_result {
        DownloadResult::Start(start) => start,
        _ => anyhow::bail!("first content not start",),
    };
    let ResponseStart {
        file_name,
        file_size,
        creation_time,
        last_modification_time,
        file_sha256: _,
        file_match_index,
        start_download_pos,
        mime: file_mime,
    } = start;

    // tracing::info!("_remote_server start file_name:{} file_size:{} creation_time:{} last_modification_time:{} file_match_index: {} start_download_pos: {}",
    //         file_name, file_size, creation_time, last_modification_time, file_match_index, start_download_pos);
    log::debug!("_remote_server start file_name:{} file_size:{} creation_time:{} last_modification_time:{} file_match_index: {} start_download_pos: {}",
     file_name, file_size, creation_time, last_modification_time, file_match_index, start_download_pos);

    let modified = if last_modification_time == 0 {
        None
    } else {
        Some(LastModified::from(
            UNIX_EPOCH + Duration::from_millis(last_modification_time),
        ))
    };

    let resp = match cond.check(modified) {
        // conditions check modified tiem
        Cond::NoBody(resp) => {
            // if no body from response
            // return_tonic_client(tunnel).await;
            log::info!("_remote_server cond.check(modified) nobody response!");
            resp
        }
        Cond::WithBody(range) => {
            // if with body from response
            let mut len = file_size;
            bytes_range(range, len)
                .map(move |(start, end)| {
                    let sub_len = end - start;
                    let s = tonic_stream(sub_len, stream, tunnel);
                    let body = Body::wrap_stream(s);

                    let mut resp = Response::new(body);

                    if sub_len != len {
                        *resp.status_mut() = StatusCode::PARTIAL_CONTENT;
                        resp.headers_mut().typed_insert(
                            ContentRange::bytes(start..end, len).expect("valid ContentRange"),
                        );

                        len = sub_len;
                    }
                    resp.headers_mut().typed_insert(ContentLength(len));
                    match file_mime.parse::<mime_guess::Mime>() {
                        Ok(mime) => {
                            resp.headers_mut().typed_insert(ContentType::from(mime));
                        }
                        Err(e) => {
                            tracing::warn!(?e, "file_mime.parse() err");
                        }
                    }
                    resp.headers_mut().typed_insert(AcceptRanges::bytes());

                    if let Some(last_modified) = modified {
                        resp.headers_mut().typed_insert(last_modified);
                    }

                    resp
                })
                .unwrap_or_else(|_BadRange| {
                    // bad byte range
                    log::info!("_remote_server received BadRage error!");
                    // destroy http item after response.
                    let client_identify =
                        format!("{}:{}", target_id.clone(), url_identify.to_owned());
                    log::info!("_remote_server end destroy http item:{:?}", client_identify);

                    let mut resp = Response::new(Body::empty());
                    *resp.status_mut() = StatusCode::RANGE_NOT_SATISFIABLE;
                    resp.headers_mut()
                        .typed_insert(ContentRange::unsatisfied_bytes(len));
                    resp
                })
        }
    };

    Ok(resp)
}

impl reject::Reject for SDKApiResult {}

async fn remote_server(
    cond: Conditionals,
    target_id: String,
    relative_path: Tail,
) -> std::result::Result<impl warp::Reply, warp::Rejection> {
    let res = _remote_server(
        cond,
        target_id,
        Some(relative_path.as_str().to_owned()),
        None,
    )
    .await
    .map_err(|e| SDKApiResult::from(e))?;
    Ok(res)
}

async fn remote_map_server(
    cond: Conditionals,
    target_id: String,
    file_id: String,
) -> std::result::Result<impl warp::Reply, warp::Rejection> {
    let res = _remote_server(cond, target_id, None, Some(file_id))
        .await
        .map_err(|e| SDKApiResult::from(e))?;
    Ok(res)
}

// start http server
async fn aync_start_http_server(
    root_path: String,
    root_dir_path: String,

    remote_path: String,
    remote_file_map: String,

    http_host: String,
    http_port: u16,

    callback: &Box<dyn HttpdImpP2PCallBack>,
) -> Result<()> {
    let root = warp::path::end().map(|| "Http Server Running OK");

    // /static/ + root_dir_path file name
    let path_map_item = warp::path(root_path).and(warp::fs::dir(root_dir_path.clone()));

    let root_dir_gen_path = root_dir_path.clone();
    let path_exit = match File::open(root_dir_gen_path.clone()) {
        Ok(_) => true,
        Err(err) => {
            if std::io::ErrorKind::AlreadyExists == err.kind() {
                true
            } else {
                false
            }
        }
    };
    if !path_exit {
        fs::create_dir_all(root_dir_gen_path.clone()).unwrap_or_else(|why| {
            log::error!(
                "http server startd listen {:?} failure {:?}",
                root_dir_gen_path,
                why.kind()
            );
        });
    }

    let symbol_path = root_dir_path.clone();
    tokio::spawn(async move {
        // performance up
        if let Err(err) = symbol_link_util::delete_symlink_in_dir(&symbol_path.to_owned()) {
            log::error!(
                "aync_start_http_server delete_symlink_in_dir error,{:?}",
                err
            );
        }
    });

    HTTP_LOCAL_ROOT_DIR.set(root_dir_path.to_owned());

    // /remote/+target param()/+ target filename
    let remote_item = warp::path(remote_path)
        .and(warp::filters::tonic::conditionals())
        .and(warp::path::param()) // param1
        .and(warp::path::tail()) // tail
        .and_then(remote_server);

    // /remote_map/+target param()+/target filemap name
    let remote_map_item = warp::path(remote_file_map)
        .and(warp::filters::tonic::conditionals())
        .and(warp::path::param()) //param
        .and(warp::path::param()) //param
        .and_then(remote_map_server);

    // GET /hi/ping => 200 OK with body "hi, ping!"
    let hello = warp::path!("hi" / String).map(|name| format!("hi,{}!", name));

    // route: path_map_item  route:remote_item and route:remote_map_item and route:hello
    let routes = warp::get().and(
        root.or(path_map_item)
            .or(remote_item)
            .or(remote_map_item)
            .or(hello),
    );

    let server = warp::serve(routes);
    let (command_tx, mut command_rx) = tokio::sync::mpsc::unbounded_channel();
    let ip_addr: IpAddr = http_host.parse()?;

    let (addr, fut) = server.try_bind_with_graceful_shutdown((ip_addr, http_port), async move {
        let mut interval = tokio::time::interval(Duration::from_secs(5));
        let tick_timeout = std::time::Duration::from_secs(15);
        let mut last_tick_instant = Instant::now();
        loop {
            tokio::select! {
                _ = interval.tick() => {
                    let _now = Instant::now();
                    log::info!("tick() run!");
                    /////////////// FIX: cpu run into deep sleep and back, need check ping_timeout.///////////////////
                    let need_stop = if _now - last_tick_instant > tick_timeout {
                        true
                    } else {
                        false
                    };
                    if need_stop {
                        log::warn!("cpu run into deep sleep and back, restart http srv!");
                        stop_http_service();
                    }
                    last_tick_instant = _now;
                },
                msg = command_rx.recv() => {
                    if let Some(msg) = msg {
                        match msg {
                            HttpCommand::StopService => {
                                // tracing::info!("HttpCommand::StopService break");
                                log::info!("HttpCommand::StopService break");
                                clean_http_items().await;

                                break;
                            }
                        }
                    } else {
                        // tracing::info!("command_rx.recv() none break");
                        log::warn!("HttpCommand::StopService command_rx.recv() none break");
                        break;
                    }
                }
            }
        }
        // tracing::info!("start_http_server loop end");
        log::info!("start_http_server loop end");
    })?;
    let host = format!("http://{}", addr);
    // let span = tracing::info_span!("Server::run", ?addr);
    // tracing::info!(parent: &span, "listening on {}", host);
    log::info!("Server::run = {},listening on {}", addr, host.clone());

    if let Err(err) = set_command_receiver(command_tx, host.clone()) {
        log::info!("aync_start_http_server errro = {}", err);
    }

    callback.on_httpd_p2p_ready(host);
    fut.await;
    Ok(())
}

pub fn stop_http_service() -> Result<()> {
    let (sender, _) = get_command_sender()?;
    sender
        .send(HttpCommand::StopService)
        .map_err(|_| anyhow::anyhow!("sender.send err receive has droped"))?;
    Ok(())
}

fn default_get_host_in_lan() -> SDKApiResult {
    match get_http_host_inner() {
        Ok(host) => {
            let file_in_lan_addr = FileInLanHttpAddress {
                is_in_lan: false,
                host: host.to_owned(),
            };
            match serde_json::to_string(&file_in_lan_addr) {
                Ok(data) => SDKApiResult::msg(data),
                Err(json_err) => {
                    log::error!("get_http_host_check_in_lan json_err : {:?}", json_err);
                    SDKApiResult::from(anyhow::anyhow!(SDKResultError::ErrorGetLanHostJsonError))
                }
            }
        }
        Err(err) => SDKApiResult::from(err),
    }
}

#[derive(Serialize, Deserialize)]
pub struct FileInLanHttpAddress {
    #[serde(rename = "is_in_lan")]
    pub is_in_lan: bool,
    #[serde(rename = "host")]
    pub host: String,
}

pub struct HttpSrvContractImp {}

impl HttpdImpP2PContract for HttpSrvContractImp {
    fn loop_block_on_httpd(
        // httpd router handle local file url path, default is "/static"
        root_path: String,
        // httpd router local root dir path.
        root_dir_path: String,
        // httpd handle remote file url path, default is "/remote"
        remote_path: String,
        // httpd handle remote file url map id
        remote_file_map: String,
        // httpd listen host, default is 0.0.0.0,means local ip address.
        http_host: String,
        // httpd listen port，defautl is 0,measns listen on random port，
        // when started ready, will callback listen address host:port by on_httpd_p2p_ready
        http_port: u16,
        // set httpd start|stop ... event callback point.
        callback: Box<dyn HttpdImpP2PCallBack>,
    ) -> SDKApiResult {
        log::info!("loop_block_on_httpd root_path = {},root_dir_path={},remote_path ={},remote_file_map={},http_host={},http_port={}",
        root_path,
        root_dir_path,
        remote_path,
        remote_file_map,
        http_host,
        http_port,
        );
        if let Err(_) = check_command_receiver() {
            return SDKApiResult::from(anyhow!(SDKResultError::ErrorHttpSrvAlreadyStarted));
        }

        if let Ok(rt) = Builder::new_multi_thread()
            .on_thread_start(|| {
                // call_attach_fn();
            })
            .on_thread_stop(|| {
                // call_detach_fn();
            })
            .enable_all()
            .build()
        {
            // block on here
            let res = rt.block_on(aync_start_http_server(
                root_path.to_owned(),
                root_dir_path.to_owned(),
                remote_path.to_owned(),
                remote_file_map.to_owned(),
                http_host.to_owned(),
                http_port,
                &callback,
            ));
            let _ = take_command_sender();
            if let Err(err) = res {
                log::info!("sync_start_http_srv res err :{} ", err);
                return SDKApiResult::from(err);
            } else {
                log::info!("sync_start_http_srv exit normal! ");
                return SDKApiResult::ok();
            }
        } else {
            SDKApiResult::from(anyhow!(SDKResultError::ErrorRuntime))
        }
    }

    fn stop_httd() -> SDKApiResult {
        if let Err(err) = stop_http_service() {
            return SDKApiResult::from(err);
        } else {
            log::info!("stop_http_srv exit normal! ");
            return SDKApiResult::ok();
        }
    }

    fn get_httpd_host() -> SDKApiResult {
        match get_http_host_inner() {
            Ok(host) => {
                log::info!("get_httpd_host ok,host = {} ", host);
                SDKApiResult::msg(host)
            }
            Err(err) => SDKApiResult::from(err),
        }
    }

    fn get_httpd_host_prefer_lan(
        remote_client_id: String,
        root_path: String,
        file_id: String,
    ) -> SDKApiResult {
        if file_id.starts_with(SYMBOL_PREFIX) {
            // parse http url from file_id, check if need alternative to lan host
            if pairinlanmap::is_remote_in_lan_map(remote_client_id.clone()).is_some() {
                let file_host_with_url_encoder: String = file_id
                    .chars()
                    .skip(SYMBOL_PREFIX.len())
                    .take(file_id.len() - SYMBOL_PREFIX.len())
                    .collect();
                let file_host_with_url = urlencoding::decode(&file_host_with_url_encoder)
                    .unwrap()
                    .to_string();

                log::info!("get_httpd_host_prefer_lan host = {:?}", file_host_with_url);
                let url = Url::parse(&file_host_with_url).unwrap();
                let mut pairs = url.query_pairs();
                let host = url.host();
                let port = url.port();
                let query_pair = pairs.next();
                if let (Some(host_unwrap), Some(port_unwrap), Some((k, v))) =
                    (host, port, query_pair)
                {
                    log::info!("get_httpd_host_prefer_lan query_pairs host ={:?},port={:?}, key = {:?},value = {:?}",host_unwrap,port_unwrap,k,v);
                    let file_name_with_base64 = v.to_string();
                    let sym_link_file_name = urlencoding::decode(&file_name_with_base64)
                        .unwrap()
                        .to_string();

                    // if host_unwrap parse from symbol link file id ,and check contains 0.0.0.0
                    let alternate_host = if host_unwrap.to_string() == r#"0.0.0.0"#.to_owned() {
                        // then get_remote_in_lan_host,if type is maybe in lan ,then query in thread
                        let inner_host = if let Some(target_ip) =
                            pairinlanmap::get_remote_in_lan_host(
                                remote_client_id.to_owned(),
                                port_unwrap,
                            ) {
                            Some(target_ip)
                        } else {
                            None
                        };
                        inner_host
                    } else {
                        None
                    };

                    if alternate_host.is_some() {
                        let lan_host = format!(
                            "http://{}:{}/{}/{}",
                            alternate_host.unwrap(),
                            port_unwrap,
                            root_path,
                            sym_link_file_name
                        );
                        log::warn!("get_httpd_host_prefer_lan lan_host = {:?}", lan_host);
                        let file_in_lan_addr = FileInLanHttpAddress {
                            is_in_lan: true,
                            host: lan_host.to_owned(),
                        };
                        match serde_json::to_string(&file_in_lan_addr) {
                            Ok(data) => {
                                return SDKApiResult::msg(data);
                            }
                            Err(json_err) => {
                                log::error!("get_httpd_host_prefer_lan  is_in_lan = true but json_err : {:?}",json_err);
                            }
                        }
                    } else {
                        log::error!("get_httpd_host_prefer_lan  alternate_host is none!");
                    }
                };
                return default_get_host_in_lan();
            } else {
                return default_get_host_in_lan();
            }
        } else {
            return default_get_host_in_lan();
        }
    }
}
